package com.swak;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.Resource;

import com.swak.converter.CoreConversionService;
import com.swak.exception.SerializeException;
import com.swak.incrementer.IdGenerator;
import com.swak.incrementer.Long64Generator;
import com.swak.serializer.JavaSerializer;
import com.swak.serializer.KryoSerializer;
import com.swak.serializer.Serializer;
import com.swak.utils.Bytes;
import com.swak.utils.RegexUtil;

import lombok.Getter;
import lombok.Setter;
import lombok.experimental.Accessors;

/**
 * 全局 App 对象
 * 
 * name: 对应应用名，可以有多个 machineID： 对应机器的唯一编码；Jav_ + Mac + MD5(path)
 * 
 * @author: lifeng
 * @date: 2020/3/29 15:39
 */
@Getter
@Setter
@Accessors(chain = true)
public final class App {

	final static Logger LOGGER = LoggerFactory.getLogger(App.class);

	private static App ME = null;
	private ApplicationContext context = null;
	private String id = "";
	private String sn = "";
	private String name = "";
	private String version = "";
	private OS os;
	private Env evn;
	private Serializer g_ser;
	private String serialization;
	private IdGenerator idGenerator;
	private ConversionService conversionService;

	/**
	 * 初始化
	 */
	public App build() {

		// 机器号
		this.id = OS.appID();

		// 系统
		this.os = OS.me();

		// 当前环境
		this.evn = context != null ? Env.valueof(context.getEnvironment().getProperty(Constants.Application_Profiles))
				: Env.dev;
		System.setProperty("app.env", this.evn.name());

		// 本机信息初始化主键
		this.buildIdGen();

		// 初始化序列化信息
		this.buildSerializer();

		// 初始化转换器
		this.buildConversionService();

		// 返回当前对象
		ME = this;
		return this;
	}

	private void buildIdGen() {
		String[] ls = RegexUtil.newRegexpMatcher("([^-]+)-([\\d]+)-([\\d]+)").getArrayGroups(sn);
		int serverSnLength = 4;
		if (ls != null && ls.length == serverSnLength) {
			idGenerator = new Long64Generator(Integer.parseInt(ls[2]), Integer.parseInt(ls[3]));
		}
	}

	private void buildSerializer() {
		switch (serialization) {
		case "kryo":
			g_ser = new KryoSerializer();
			break;
		case "kryo_pool":
			g_ser = new KryoSerializer();
			break;
		default:
			g_ser = new JavaSerializer();
			break;
		}
	}

	private void buildConversionService() {
		CoreConversionService.getContext();
	}

	/**
	 * 当前App
	 *
	 * @return APP
	 */
	public static App me() {
		return ME;
	}

	/**
	 * 获得代理类
	 * 
	 * @param <T>
	 * @param requiredType
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getProxy(Class<T> requiredType) {
		Class<?>[] interfacess = requiredType.getInterfaces();
		if (interfacess != null && interfacess.length > 0) {
			return (T) App.getBean(interfacess[0]);
		}
		return App.getBean(requiredType);
	}

	/**
	 * 获取 Bean
	 *
	 * @param requiredType bean type
	 * @return Bean
	 */
	public static <T> T getBean(Class<T> requiredType) {
		if (ME != null && ME.context != null) {
			try {
				return ME.context.getBean(requiredType);
			} catch (Exception ignored) {
				LOGGER.error("Get Bean Error {}", ignored);
			}
		}
		return null;
	}

	/**
	 * 自动注入指定 Bean 中的依赖
	 * 
	 * @param bean
	 */
	public static void autowireBean(Object bean) {
		if (ME != null && ME.context != null) {
			ME.context.getAutowireCapableBeanFactory().autowireBean(bean);
		}
	}

	/**
	 * 获取资源文件
	 * <p>
	 * classpath:localtion file:localtion
	 *
	 * @param localtion 资源路劲
	 * @return 资源
	 */
	public static Resource resource(String localtion) {
		if (ME != null && ME.context != null) {
			return ME.context.getResource(localtion);
		}
		return null;
	}

	/**
	 * 获取资源文件 -- 以文件流的方式获取（可以获取jar中的资源）
	 *
	 * @param localtion 资源文件
	 * @return 流
	 */
	public static InputStream inputStream(String localtion) {
		try {
			Resource resource = App.resource(localtion);
			if (resource.isFile()) {
				return new FileInputStream(resource.getFile());
			}
			return resource.getInputStream();
		} catch (IOException e) {
			return null;
		}
	}

	/**
	 * 获取资源文件 -- 以文件流的方式获取（可以获取jar中的资源）
	 *
	 * @param localtion 资源文件
	 * @return 流
	 */
	public static byte[] bytes(String localtion) {
		InputStream inputStream = App.inputStream(localtion);
		try {
			if (inputStream != null) {
				return Bytes.bytes(inputStream);
			}
		} catch (Exception e) {
		}
		return null;
	}

	/**
	 * 获取资源文件 -- 以文件的方式获取(不能获取jar中的资源)
	 *
	 * @param localtion 资源文件
	 * @return 流
	 */
	public static File file(String localtion) {
		try {
			Resource resource = App.resource(localtion);
			if (resource.isFile()) {
				return resource.getFile();
			}
			return null;
		} catch (IOException e) {
			return null;
		}
	}

	/**
	 * 获取资源文件 -- 以文件的方式获取(不能获取jar中的资源)
	 *
	 * @param localtion 资源文件
	 * @return 流
	 */
	public static File file(String localtion, boolean mks) {
		File file = App.file(localtion);
		if (file != null && !file.exists() && mks) {
			file.mkdirs();
		}
		return file;
	}

	/**
	 * 调用实际的序列化工具 -- 序列化
	 *
	 * @param obj 对象
	 * @return 字节数组
	 * @throws SerializeException 序列化错误
	 */
	public static byte[] serialize(Object obj) throws SerializeException {
		return App.ME.g_ser.serialize(obj);
	}

	/**
	 * 调用实际的序列化工具 -- 反序列化
	 *
	 * @param bytes 字节数组
	 * @return 对象
	 * @throws SerializeException 序列化错误
	 */
	public static Object deserialize(byte[] bytes) throws SerializeException {
		return bytes != null ? App.ME.g_ser.deserialize(bytes) : null;
	}

	/**
	 * 生成主键
	 * 
	 * @param <T> 类型
	 * @return 主键
	 */
	public static <T> T genId() {
		return App.ME.idGenerator.id();
	}
}